import sys
import os
import datetime
import queue

from multiprocessing import Process

from scripts.sources.scripts.utility.log import Logger

logger = Logger()


class Schedule(object):
    def __init__(self):
        """
        task_dict = {
            id: {
                'name': xxxx.py,
                execute_conf: {
                    'every_sec': '123',
                    'sec_of_min': '1', 0-59
                    'min_of_hour': '*', 0-59
                    'hour_of_day': '*', 0-23
                },
                task_queue: [],
                current_task: P,
                next_execute_time: '',
                expire_set: '',
                expire_time: '' # not used
            }
        }
        """
        self.base_dir = os.path.dirname(__file__)
        self.scripts_path = os.path.join(self.base_dir, 'scripts')
        self.config_path = os.path.join(self.base_dir, 'settings.sc')
        self.now_time = datetime.datetime.now()
        self.task_dict = {}

        self.timeout = 5000

        self._read_task_config()

    def _read_task_config(self):
        try:
            with open(self.config_path, 'r') as f:
                conf = f.readlines()[2:]

            for _conf in conf:
                _data = _conf[:-1].split(' ')
                if self.task_dict.get(_data[0]):
                    raise ValueError(f"duplicated id '{_data[0]}'")

                if _data[0] in ['#', '']:
                    continue

                _temp_dict = dict()
                _temp_dict['name'] = _data[1]
                _temp_dict['task_queue'] = queue.Queue()
                _temp_dict['expire_set'] = datetime.timedelta(seconds=int(_data[2]))

                _temp_dict['execute_conf'] = dict()
                self._set_execute_conf(_data[3:], _temp_dict['execute_conf'])

                self._set_next_execute_time(_temp_dict)

                self.task_dict[_data[0]] = _temp_dict
        except Exception as e:
            logger.error("'settings.sc error'")
            logger.exception(e)
            sys.exit(1)

    def run(self):
        while True:
            try:
                self.now_time = datetime.datetime.now()
                for _id, _task_config in self.task_dict.items():
                    if _task_config['next_execute_time'] <= self.now_time:
                        with open(os.path.join(self.scripts_path, _task_config['name']), 'r') as f:
                            p = Process(target=exec, args=(f.read(),))

                        _task_config['expire_time'] = _task_config['expire_set'] + self.now_time
                        _task_config['task_queue'].put(p)
                        self._set_next_execute_time(_task_config)

                    if not _task_config['task_queue'].empty() and \
                            (not _task_config.get('current_task') or not _task_config['current_task'].is_alive()):
                        _task_config['current_task'] = _task_config['task_queue'].get()
                        _task_config['current_task'].start()

                    if _task_config.get('current_task') and not _task_config['current_task'].is_alive():
                        _task_config['current_task'] = None

            except Exception as e:
                logger.exception(e)

    @staticmethod
    def _set_execute_conf(data, execute_conf):
        execute_conf['every_sec'] = data[0]
        execute_conf['sec_of_min'] = data[1]
        execute_conf['min_of_hour'] = data[2]
        execute_conf['hour_of_day'] = data[3]

    def _set_next_execute_time(self, conf):
        execute_conf = conf['execute_conf']
        very_second = execute_conf['every_sec']
        if very_second and very_second != '*':
            conf['next_execute_time'] = self.now_time + datetime.timedelta(seconds=int(very_second))
        else:
            time_tag_list = list(execute_conf.keys())[:0:-1]
            now_dict = {
                'sec_of_min': self.now_time.second,
                'min_of_hour': self.now_time.minute,
                'hour_of_day': self.now_time.hour,
            }
            changed_tag = None

            execute_dict = dict()

            for i, tag in enumerate(time_tag_list):
                if execute_conf[tag] != '*':
                    execute_dict[tag] = int(execute_conf[tag])
                    if not changed_tag:
                        changed_tag = tag
                else:
                    if i != 0 and execute_conf[time_tag_list[i-1]] != '*':
                        execute_dict[tag] = 0
                    else:
                        execute_dict[tag] = now_dict[tag]

            if not changed_tag:
                raise ValueError("'sec_of_min', 'min_of_hour' and 'hour_of_day' are all null.")
            conf['next_execute_time'] = datetime.datetime.strptime(
                f"{self.now_time.year}-{self.now_time.month}-{self.now_time.day}"
                f" {execute_dict['hour_of_day']}-{execute_dict['min_of_hour']}-{execute_dict['sec_of_min']}",
                "%Y-%m-%d %H-%M-%S")

            # prefix execute time.
            if conf['next_execute_time'] < self.now_time:
                if changed_tag == 'sec_of_min':
                    conf['next_execute_time'] += datetime.timedelta(seconds=60)
                elif changed_tag == 'min_of_hour':
                    conf['next_execute_time'] += datetime.timedelta(minutes=60)
                elif changed_tag == 'hour_of_day':
                    conf['next_execute_time'] += datetime.timedelta(hours=24)

    def _push_task(self):
        pass

    def _execute_task(self):
        for _, _task_config in self.task_dict.items():
            pass
            # if _task_config['expire_time'] <= self.now_time:
            #     _task_config['current_task'].kill()
            #     _task_config['current_task'] = None

            # execute task

    def _kill_task(self):
        pass


def main():
    schedule = Schedule()
    schedule.run()
    print(0)


if __name__ == '__main__':
    main()
